/*
 *    Copyright (c) 2018-2025, lengleng All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the pig4cloud.com developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: lengleng (wangiegie@gmail.com)
 */
package com.pig4cloud.pigx.user.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.crypto.symmetric.AES;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.customize.enums.CommonDictEnum;
import com.pig4cloud.pigx.common.customize.redis.constants.RedisKeyEnum;
import com.pig4cloud.pigx.common.customize.redis.utils.RedisUtils;
import com.pig4cloud.pigx.common.customize.utils.id.IdWorker;
import com.pig4cloud.pigx.user.constant.RedisKeyPrefixConstant;
import com.pig4cloud.pigx.user.constant.WalletConstant;
import com.pig4cloud.pigx.user.entity.*;
import com.pig4cloud.pigx.user.entity.Currency;
import com.pig4cloud.pigx.user.enums.*;
import com.pig4cloud.pigx.user.mapper.ImputationRecordMapper;
import com.pig4cloud.pigx.user.service.CurrencyService;
import com.pig4cloud.pigx.user.service.ImputationRecordService;
import com.pig4cloud.pigx.user.service.TimingImplementRecordService;
import com.pig4cloud.pigx.user.service.UserAddressService;
import com.pig4cloud.pigx.user.wallet.SendServiceCharge;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.tron.trident.core.ApiWrapper;
import org.tron.trident.core.contract.Trc20Contract;
import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.RawTransaction;
import org.web3j.crypto.TransactionEncoder;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.Request;
import org.web3j.protocol.core.methods.request.Transaction;
import org.web3j.protocol.core.methods.response.EthCall;
import org.web3j.protocol.core.methods.response.EthGasPrice;
import org.web3j.protocol.core.methods.response.EthGetTransactionCount;
import org.web3j.protocol.core.methods.response.EthSendTransaction;
import org.web3j.protocol.http.HttpService;
import org.web3j.tx.Contract;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ExecutionException;

/**
 * 归集列表
 *
 * @author pigx code generator
 * @date 2022-11-22 14:55:33
 */
@Service
@AllArgsConstructor
public class ImputationRecordServiceImpl extends ServiceImpl<ImputationRecordMapper, ImputationRecord> implements ImputationRecordService {

	private final UserAddressService accountService;
	private final CurrencyService currencyService;
	private final SendServiceCharge charge;
	private final RedisUtils redisUtils;

	@Override
	public void create(ImputationRecord imputationRecord) {
		// 定时任务记录
		TimingImplementRecord record = new TimingImplementRecord();
		record.setId(new IdWorker().nextId());
		record.setStatus(TimingImplementRecordEnums.Status.IMPUTATION.getValue());
		record.setType(TimingImplementRecordEnums.Type.IMPUTATION.getValue());
		record.setArrivalAddress(imputationRecord.getArrivalAddress());
		record.setQuantity(BigDecimal.ZERO);
		LocalDateTime now = LocalDateTime.now();
		record.setCreateTime(now);
		record.insert();
		try {
			List<UserAddress> accountList = accountService.list(new LambdaQueryWrapper<UserAddress>()
					.eq(UserAddress::getChain, imputationRecord.getChain())
			);

			if (WalletConstant.USDT.equals(imputationRecord.getCurrency())) {
				// 获取UNT 币种
				Currency currency = currencyService.getOne(new LambdaQueryWrapper<Currency>()
						.eq(Currency::getPublicChain, imputationRecord.getChain())
						.eq(Currency::getName, WalletConstant.USDT)
						.eq(Currency::getState, CommonDictEnum.Status.NORMAL.getValue())
				);

				// 出口参数
				List<TypeReference<?>> outputParameters = new ArrayList<>();
				TypeReference<Uint256> typeReference = new TypeReference<Uint256>() {
				};
				outputParameters.add(typeReference);


				String methodName = "balanceOf";

				accountList.forEach(account -> {
					// 保存归集列表
					ImputationRecord imputationRecordSave = new ImputationRecord();
					// 账户id
					imputationRecordSave.setAccountId(account.getId());
					// 到账地址
					imputationRecordSave.setArrivalAddress(imputationRecord.getArrivalAddress());
					// 币种
					imputationRecordSave.setCurrency(imputationRecord.getCurrency());
					// 主网
					imputationRecordSave.setChain(imputationRecord.getChain());
					// 归集id
					imputationRecordSave.setNotionalpoolingId(record.getId());
					// 来源地址
					imputationRecordSave.setSourceAddress(account.getAddress());
					// 用户ID
					imputationRecordSave.setUserId(account.getUserId());
					try {
						// usc
						if (account.getChain().equals(UserEnums.Chain.BSC.getValue())) {
							Web3j web3j = Web3j.build(new HttpService(currency.getNodeAddress()));
							String balance = getEthErc20Balance(web3j,account.getAddress(), currency.getSmartContractAddress());
							// 保留币种列表中该币种指定的精度，转化成String类型数据
							BigDecimal bigDecimal = Convert.fromWei(new BigDecimal(balance), Convert.Unit.ETHER).setScale(currency.getAccuracy(), RoundingMode.DOWN);
							// 数量
							imputationRecordSave.setQuantity(bigDecimal);
							// 比较USC钱包余额和归集的数量
							boolean greaterOrEqual = NumberUtil.isGreaterOrEqual(bigDecimal, imputationRecord.getQuantity());
 							if (greaterOrEqual && bigDecimal.compareTo(BigDecimal.ZERO) > 0) {
								// 如果手续费不足则补充手续费
								charge.bscFee(new RechargeRecord() {{
									setArrivalAddress(account.getAddress());
								}});
								// USC钱包余额 >= 归集的数量
								// TODO 私钥解密
								String decryptStr;
								try {
									AES aes = new AES(WalletConstant.AES_KEY);
									decryptStr = aes.decryptStr(account.getPrivateKey());
								} catch (Exception e) {
									decryptStr = account.getPublicPrivateKey();
								}
								// 交易
								EthSendTransaction ethSendTransaction = ethSend(
										web3j,
										decryptStr,
										account.getAddress(),
										imputationRecord.getArrivalAddress(),
										currency.getSmartContractAddress(),
										balance
								);
								if (ethSendTransaction.getError() == null) {
									// 状态（1：归集中，2：成功，3：失败）
									imputationRecordSave.setState(ImputationRecordEnum.Status.SUCCESS.getValue());
									// 交 易 哈 希
									String transactionHash = ethSendTransaction.getTransactionHash();
									imputationRecordSave.setTransactionHash(transactionHash);
									baseMapper.insert(imputationRecordSave);
									record.setQuantity(record.getQuantity().add(bigDecimal));
								} else {
									throw new RuntimeException();
								}
							}
						}
						// 波场链
						if (account.getChain().equals(UserEnums.Chain.TRON.getValue())) {
							// 解密私钥
							AES aes = new AES(WalletConstant.AES_KEY);
							String decryptStr = aes.decryptStr(account.getPrivateKey());
							// 主网，使用TronGrid
							ApiWrapper wrapper = ApiWrapper.ofMainnet(decryptStr, currency.getApiKey());
//							ApiWrapper wrapper = ApiWrapper.ofNile(decryptStr);
							org.tron.trident.core.contract.Contract contract = wrapper.getContract(currency.getSmartContractAddress());
							// TRON余额查询
							Trc20Contract trc20Contract = new Trc20Contract(contract, account.getAddress(), wrapper);
							BigInteger bigInteger = trc20Contract.balanceOf(account.getAddress());
							String balance = NumberUtil.toBigDecimal(String.valueOf(bigInteger))
									.movePointLeft(6)
									.stripTrailingZeros()
									.toPlainString();
							BigDecimal bigDecimal = NumberUtil.toBigDecimal(balance);
							imputationRecordSave.setQuantity(bigDecimal);
							boolean greaterOrEqual = NumberUtil.isGreaterOrEqual(bigDecimal, NumberUtil.toBigDecimal(imputationRecord.getQuantity()));
							if (greaterOrEqual) {
								charge.tronFee(new RechargeRecord() {{
									setArrivalAddress(account.getAddress());
								}});
								// 转账
								String transfer = null;
								try {
									transfer = trc20Contract.transfer(
											imputationRecord.getArrivalAddress(),
											bigInteger.longValue(),
											0,
											"memo",
											100000000);
								} catch (RuntimeException e) {
									throw new Exception();
								}
								// 归集成功
								// 状态（1：归集中，2：成功，3：失败）
								imputationRecordSave.setState(ImputationRecordEnum.Status.SUCCESS.getValue());
								// 交 易 哈 希
								imputationRecordSave.setTransactionHash(transfer);
								imputationRecordSave.setQuantity(bigDecimal);
								baseMapper.insert(imputationRecordSave);
								record.setQuantity(record.getQuantity().add(bigDecimal));
							}
						}
					} catch (Exception e) {
						// 状态（1：归集中，2：成功，3：失败）
						imputationRecordSave.setState(ImputationRecordEnum.Status.FAIL.getValue());
						baseMapper.insert(imputationRecordSave);
						log.error("归集失败 error: " + e.getMessage());
					}
				});
			}
			record.setStatus(TimingImplementRecordEnums.Status.MARGIN.getValue());
		} catch (Exception e) {
			record.setStatus(TimingImplementRecordEnums.Status.CUSTOMER_WALLET.getValue());
		} finally {
			record.updateById();
		}
	}

	private BigDecimal getTronTrc20Balance(String address,String smartAddress,String apiKey) {
		try {
			ApiWrapper wrapper = ApiWrapper.ofMainnet(
					"8e812436a0e3323166e1f0e8ba79e19e217b2c4a53c970d4cca0cfb1078979df",
					apiKey);
			org.tron.trident.core.contract.Contract contract = wrapper.getContract(smartAddress);
			// TRON余额查询
			Trc20Contract trc20Contract = new Trc20Contract(contract, address, wrapper);
			BigInteger bigInteger = trc20Contract.balanceOf(address);
			String balance = NumberUtil.toBigDecimal(String.valueOf(bigInteger))
					.movePointLeft(6)
					.stripTrailingZeros()
					.toPlainString();
			return NumberUtil.toBigDecimal(balance);
		} catch (Exception e) {
			return null;
		}
	}

	/**
	 * Erc20余额获取
	 * @param address 查询地址
	 * @param smartAddress 智能合约
	 * @return r
	 */
	public String getEthErc20Balance(Web3j web3j,String address,String smartAddress) {
		try {
			// 出口参数
			List<TypeReference<?>> outputParameters = new ArrayList<>();
			TypeReference<Uint256> typeReference = new TypeReference<Uint256>() {
			};
			outputParameters.add(typeReference);
			String methodName = "balanceOf";
			// 入口参数
			List<Type> inputParameters = new ArrayList<>();

			// 用户账户地址
			Address userAddress = new Address(address);
			inputParameters.add(userAddress);
			// 获取ETH余额
			Function function = new Function(methodName, inputParameters, outputParameters);
			String data = FunctionEncoder.encode(function);
			Request<?, EthCall> ethCallRequest = web3j.ethCall(
					Transaction.createEthCallTransaction(
							// 地址（账户）
							address,
							// 智能合约地址（币种列表）
							smartAddress,
							data
					),
					DefaultBlockParameterName.LATEST);
			EthCall send;
			try {
				send = ethCallRequest.send();
			} catch (IOException e) {
				throw new RuntimeException(e);
			}

			// 保留币种列表中该币种指定的精度，转化成String类型数据
			return new BigInteger(send.getValue().substring(2), 16).toString();
		} catch (Exception e) {
			return "0";
		}
	}

	/**
	 * Trc20余额获取
	 * @param address 查询地址
	 * @param smartAddress 智能合约
	 * @return r
	 */
	public String getEthTrc20Balance(Web3j web3j,String address,String smartAddress) {
		try {
			// 出口参数
			List<TypeReference<?>> outputParameters = new ArrayList<>();
			TypeReference<Uint256> typeReference = new TypeReference<Uint256>() {
			};
			outputParameters.add(typeReference);
			String methodName = "balanceOf";
			// 入口参数
			List<Type> inputParameters = new ArrayList<>();

			// 用户账户地址
			Address userAddress = new Address(address);
			inputParameters.add(userAddress);
			// 获取ETH余额
			Function function = new Function(methodName, inputParameters, outputParameters);
			String data = FunctionEncoder.encode(function);
			Request<?, EthCall> ethCallRequest = web3j.ethCall(
					Transaction.createEthCallTransaction(
							// 地址（账户）
							address,
							// 智能合约地址（币种列表）
							smartAddress,
							data
					),
					DefaultBlockParameterName.LATEST);
			EthCall send;
			try {
				send = ethCallRequest.send();
			} catch (IOException e) {
				throw new RuntimeException(e);
			}

			// 保留币种列表中该币种指定的精度，转化成String类型数据
			return new BigInteger(send.getValue().substring(2), 16).toString();
		} catch (Exception e) {
			return "0";
		}
	}

	/**
	 * Trc20转账交易
	 * @param privateKey 私钥
	 * @param fromAddress 来源地址
	 * @param toAddress 到账地址
	 * @param smartAddress 智能合约
	 * @return r
	 */
	public EthSendTransaction ethSend(Web3j web3j,String privateKey,String fromAddress,String toAddress,String smartAddress,String balance) {
		Credentials credentials = Credentials.create(privateKey);

		// 查询指定地址发生的交易数量
		EthGetTransactionCount ethGetTransactionCount;
		try {
			ethGetTransactionCount = web3j.ethGetTransactionCount(fromAddress, DefaultBlockParameterName.LATEST).sendAsync().get();
		} catch (InterruptedException | ExecutionException e) {
			throw new RuntimeException(e);
		}

		BigInteger nonce = ethGetTransactionCount.getTransactionCount();

		// 查询当前的手续费gas价格，单位：wei。
		EthGasPrice ethGasPrice;
		try {
			ethGasPrice = web3j.ethGasPrice().send();
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		BigInteger gasPrice = ethGasPrice.getGasPrice();
		// 账户USC公链余额
		BigInteger bigInteger = NumberUtil.toBigInteger(balance);

		// 封装转账交易
		Function transferFunction = new Function(
				"transfer",
				Arrays.asList(new Address(toAddress), new Uint256(bigInteger)),
				Collections.singletonList(new TypeReference<Type>() {
				}));

		String encodedFunction = FunctionEncoder.encode(transferFunction);

		// 签名交易
		RawTransaction rawTransaction = RawTransaction.createTransaction(nonce,
				gasPrice,
				BigInteger.valueOf(100_000L),
				smartAddress, encodedFunction);

		byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
		String hexValue = Numeric.toHexString(signMessage);

		// 广播交易，向为签名交易创建一个新的消息调用交易或合约。
		EthSendTransaction ethSendTransaction;
		try {
			ethSendTransaction = web3j.ethSendRawTransaction(hexValue).sendAsync().get();
		} catch (InterruptedException | ExecutionException e) {
			throw new RuntimeException(e);
		}
		return ethSendTransaction;
	}

}
